import java.math.BigInteger;
import java.util.ArrayList;

public class Biblioteca {
	
	// Atributos
	private String nombre;
	private int plazo=7;
	private ArrayList<Libro> listaLibros = new ArrayList<Libro>();
	private ArrayList<Editorial> listaEditoriales = new ArrayList<Editorial>();
	private ArrayList<Tipo> listaTipos = new ArrayList<Tipo>();
	private ArrayList<Usuario> listaUsuarios = new ArrayList<Usuario>();
	

/* ################# METODOS PARA MANIPULACION DE BIBLIOTECA ################# */
	
	// Contructor
	public Biblioteca(String nombre){
		this.modificarNombre(nombre);
	}
	
	/* INTERFAZ PUBLICA */
/*..............................................................................................*/
	public void modificarNombre(String nombre) {
		this.nombre = nombre;
	}

/*..............................................................................................*/
	public boolean modificarPlazo(int plazo){
		if(plazo>0){
			this.setPlazo(plazo);
			return true;
		}
		else
		{
			System.out.println("El Plazo especificado es inválido.");
			return false;
		}
	}
	
/* ################# METODOS PARA MANIPULACION DE ITEMS ################# */
	private boolean existeEnBase(Item item){
		ArrayList<Item> base = null;
		if(item instanceof Libro){base = new ArrayList<Item>(getListaLibros());}
		else if(item instanceof Editorial){base = new ArrayList<Item>(getListaEditoriales());}
		else if(item instanceof Tipo){base = new ArrayList<Item>(getListaTipos());}
		else if(item instanceof Usuario){base = new ArrayList<Item>(getListaUsuarios());}
		else {
			System.out.println("El Objeto Parametro no corresponde a ninguna subclase valida.");
			return false;
			};
			
		for (Item actual : base){
			if(actual==item){
				return true;
			}
		}
		
		return false;
		
		}

	
/* ################# METODOS PARA MANIPULACION DE USUARIO ################# */
	
	/* INTERFAZ PUBLICA */
/*..............................................................................................*/	
	public boolean registrarUsuario(int dni, String nombre,String apellido,int dia,int mes,int anio,String direccion,int codigoPostal,String ciudad, int telefono){
		Usuario nuevo = new Usuario(dni, nombre, apellido, dia, mes, anio, direccion, codigoPostal, ciudad, telefono);
		if(this.registrarUsuario(nuevo)){
			return true;
		}
		else
		{
			return false;
		}
	}
/*..............................................................................................*/
	private boolean registrarUsuario(Usuario nuevo){
		if(this.buscarUsuario(nuevo.getDni())==null){
			this.listaUsuarios.add(nuevo);
			return true;
		}
		else
		{
			System.out.println("\nError al registrar el usuario: "+nuevo.getApellido()+", "+nuevo.getNombre()+". DNI ya existe.");
			return false;
		}
	}

/*..............................................................................................*/
	public boolean deshabilitarUsuario(int dni){
		if(this.buscarUsuario(dni)!=null){
			this.buscarUsuario(dni).deshabilitar();
			return true;
		}
		else
		{
			return false;
		}
	}

/*..............................................................................................*/
	public boolean habilitarUsuario(int dni){
		if(this.buscarUsuario(dni)!=null){
			this.buscarUsuario(dni).habilitar();
			return true;
		}
		else
		{
			return false;
		}
	}

/*..............................................................................................*/
	public boolean estaHabilitadoUsuario(int dni){
		if(this.obtenerUsuario(dni)!=null){
			return this.obtenerUsuario(dni).estaHabilitado();
		}
		else
		{
			System.out.println("Error. No existe Usuario con DNI: "+dni);
			return false;
		}
	}
	
/*..............................................................................................*/	
	public boolean modificarUsuario(int dni, int dniCorrecto, String nombre,String apellido,int dia,int mes,int anio,String direccion,int codigoPostal,String ciudad,int telefono){
		if(this.buscarUsuario(dni)!=null && this.buscarUsuario(dniCorrecto)==null){
			this.buscarUsuario(dni).modificar(dniCorrecto, nombre, apellido, dia, mes, anio, direccion, codigoPostal, ciudad, telefono);
			return true;
		}
		else
		{
			System.out.println("Error al intentar modificar usuario con DNI: "+ dni +". Posiblemente no exista o el nuevo DNI solicitado: "+dniCorrecto+" ya este registrado.");
			return false;
		}
		
	}

/*..............................................................................................*/
	public void imprimirInfractores(){
		System.out.println("\nInfractores:");
		for (Usuario actual : this.buscarInfractores()) {
			actual.imprimirDatos();
		}
	}

/*..............................................................................................*/
	public ArrayList<Usuario> imprimirUsuarios(){
		ArrayList<Usuario> copia = new ArrayList<Usuario>();
		System.out.println("\nTodos los Usuarios:");
		for (Usuario actual : this.getListaUsuarios()) {
			actual.imprimirDatos();
			copia.add((Usuario) actual.clone());
		}
		return copia;
	}
	
	/* Metodos Privados */
/*..............................................................................................*/	
	private Usuario buscarUsuario(int dni){
		for (Usuario actual : this.getListaUsuarios()) {
			if (actual.getDni()==dni){
				return actual;
			}
		}
		return null;
	}

/*..............................................................................................*/	
	public Usuario obtenerUsuario(int dni){
		for (Usuario actual : this.getListaUsuarios()) {
			if (actual.getDni()==dni){
				return (Usuario) actual.clone();
			}
		}
		System.out.println("Error. No se puede realizar la copia del usuario con DNI: "+dni+" ya que no existe.");
		return null;
	}
	
/*..............................................................................................*/	
	private ArrayList<Usuario> buscarInfractores() {
		ArrayList<Usuario> infractores = new ArrayList<Usuario>();
		boolean repetido;

		// Recorremos todos los libros y sus prestamos
		for (Libro libroActual : this.getListaLibros()) {
			for (Usuario usuarioActual : libroActual.buscarInfractores()) {
				repetido=false;
				for (Usuario usuarioYaProcesado : infractores) {
					if(usuarioActual == usuarioYaProcesado){
						repetido=true;
					}
				}
				if(!repetido){infractores.add(usuarioActual);};
			}
		}
		
			return infractores;
			
	}
	
/* ################# METODOS PARA MANIPULACION DE TIPO ################# */ 
	
	/* INTERFAZ PUBLICA */
/*..............................................................................................*/
	public boolean registrarTipo(Integer codigo, String descripcion){
		
		if((this.buscarTipo(codigo)==null)&&(this.buscarTipo(descripcion)==null)){
			Tipo nuevo = new Tipo(codigo,descripcion);
			this.listaTipos.add(nuevo);
			return true;
		}
		else
		{
			System.out.println("\nError al registrar el tipo: "+descripcion+", codigo o descripción ya existe.");
			return false;
		}
	}

/*..............................................................................................*/
	public boolean deshabilitarTipo(int codigo){
		if(this.buscarTipo(codigo)!=null){
			this.buscarTipo(codigo).deshabilitar();
			return true;
		}
		else
		{
			return false;
		}
	}

/*..............................................................................................*/
	public boolean habilitarTipo(int codigo){
		if(this.buscarTipo(codigo)!=null){
			this.buscarTipo(codigo).habilitar();
			return true;
		}
		else
		{
			return false;
		}
	}

/*..............................................................................................*/	
	public Tipo obtenerTipo(int codigo){
		for (Tipo actual : this.getListaTipos()) {
			if (actual.getCodigo()==codigo){
				return (Tipo) actual.clone();
			}
		}
		System.out.println("Error. No se puede realizar la copia del Tipo con Identificador: "+codigo+" ya que no existe.");
		return null;
	}
	
/*..............................................................................................*/
	public boolean estaHabilitadoTipo(int codigo){
		if(this.obtenerTipo(codigo)!=null){
			return this.obtenerTipo(codigo).estaHabilitado();
		}
		else
		{
			System.out.println("Error. No existe Tipo con codigo: "+codigo);
			return false;
		}
	}
	
/*..............................................................................................*/
	public boolean modificarTipo(int codigo, int codigoCorrecto, String descripcion){
		if(this.buscarTipo(codigo)!=null && this.buscarTipo(codigoCorrecto)==null){
			this.buscarTipo(codigo).modificar(codigoCorrecto, descripcion);
			return true;
		}
		else
		{
			System.out.println("Error al intentar modificar el Tipo con codigo: "+ codigo +". Posiblemente no exista o el nuevo codigo solicitado: "+codigoCorrecto+" ya este registrado.");
			return false;
		}
	}
	
/*..............................................................................................*/
	public ArrayList<Tipo> imprimirTipos(){
		ArrayList<Tipo> copia = new ArrayList<Tipo>();
		System.out.println("\nTodos los Tipos:");
		for (Tipo actual : this.getListaTipos()) {
			actual.imprimirDatos();
			copia.add((Tipo) actual.clone());
		}
		return copia;
	}
	
	/* METODOS PRIVADOS */
/*..............................................................................................*/
	private Tipo buscarTipo(Integer codigo){
		for (Tipo actual : this.getListaTipos()) {
			if((actual.getCodigo()==codigo)&&(actual.estaHabilitado())){
				return actual;
			}
		}
		return null;
	}
/*..............................................................................................*/
	public Tipo buscarTipo(String descripcion){
		for (Tipo actual : this.getListaTipos()) {
			if((actual.getDescripcion().compareTo(descripcion)==0)&&(actual.estaHabilitado())){
				return actual;
			}
		}
		return null;
	}
/*..............................................................................................*/
	public Tipo buscarTipo(Integer codigo, String descripcion){
		if(this.buscarTipo(codigo)==this.buscarTipo(descripcion)){
			return this.buscarTipo(codigo); 
		}
		else
		{
			return null;
		}
	}
	
/* ################# METODOS PARA MANIPULACION DE EDITORIAL ################# */
	/* INTERFAZ PUBLICA */
	public boolean registrarEditorial(int identificador,String nombre){
		
		if((this.buscarEditorial(identificador)==null)&&(this.buscarEditorial(nombre)==null)){
			Editorial nueva = new Editorial(identificador,nombre);
			this.listaEditoriales.add(nueva);
			return true;
		}
		else
		{
			System.out.println("\nError al registrar la editorial: "+nombre+", Identificador o nombre ya existe.");
			return false;
		}
		
	}

/*..............................................................................................*/
		public boolean deshabilitarEditorial(int identificador){
			if(this.buscarEditorial(identificador)!=null){
				this.buscarEditorial(identificador).deshabilitar();
				return true;
			}
			else
			{
				return false;
			}
		}

/*..............................................................................................*/
		public boolean habilitarEditorial(int identificador){
			if(this.buscarEditorial(identificador)!=null){
				this.buscarEditorial(identificador).habilitar();
				return true;
			}
			else
			{
				return false;
			}
		}

/*..............................................................................................*/	
		public Editorial obtenerEditorial(int identificador){
			for (Editorial actual : this.getListaEditoriales()) {
				if (actual.getIdentificador()==identificador){
					return (Editorial) actual.clone();
				}
			}
			System.out.println("Error. No se puede realizar la copia del Editorial con Identificador: "+identificador+" ya que no existe.");
			return null;
		}
		
/*..............................................................................................*/
		public boolean estaHabilitadoEditorial(int identificador){
			if(this.obtenerEditorial(identificador)!=null){
				return this.obtenerEditorial(identificador).estaHabilitado();
			}
			else
			{
				System.out.println("Error. No existe Editorial con identificador: "+identificador);
				return false;
			}
		}
		
/*..............................................................................................*/
		public boolean modificarEditorial(int identificador, int identificadorCorrecto, String nombre){
			if(this.buscarEditorial(identificador)!=null && this.buscarEditorial(identificadorCorrecto)==null){
				this.buscarEditorial(identificador).modificar(identificadorCorrecto, nombre);
				return true;
			}
			else
			{
				System.out.println("Error al intentar modificar la Editorial con identificador: "+ identificador +". Posiblemente no exista o el nuevo identificador solicitado: "+identificadorCorrecto+" ya este registrado.");
				return false;
			}
		}

/*..............................................................................................*/
		public ArrayList<Editorial> imprimirEditoriales(){
			ArrayList<Editorial> copia = new ArrayList<Editorial>();
			System.out.println("\nTodas las Editoriales:");
			for (Editorial actual : this.getListaEditoriales()) {
				actual.imprimirDatos();
				copia.add((Editorial) actual.clone());
			}
			return copia;
		}

	/* Metodos Privados */
/*..............................................................................................*/
	private Editorial buscarEditorial(Integer identificador){
		for (Editorial actual : this.getListaEditoriales()) {
			if((actual.getIdentificador()==identificador)){
				return actual;
			}
		}
		return null;
	}

/*..............................................................................................*/	
	private Editorial buscarEditorial(String nombre){
		for (Editorial actual : this.getListaEditoriales()) {
			if((actual.getNombre().compareTo(nombre)==0)){
				return actual;
			}
		}
		return null;
	}

/*..............................................................................................*/
	private Editorial buscarEditorial(int identificador,String nombre){
		if(this.buscarEditorial(identificador)==this.buscarEditorial(nombre)){
			return this.buscarEditorial(identificador); 
		}
		else
		{
			return null;
		}
	}
	
/* ################# METODOS PARA MANIPULACION DE LIBROS ################# */
	/* INTERFAZ PUBLICA */
	public boolean registrarLibro(BigInteger isbn, String titulo, int anioPublicacion,int identificadorEditorial, int codigoTipo){
		if(this.buscarLibro(isbn)==null && this.buscarEditorial(identificadorEditorial)!=null && this.buscarTipo(codigoTipo)!=null && this.buscarEditorial(identificadorEditorial).estaHabilitado() && this.buscarTipo(codigoTipo).estaHabilitado()){
			Libro nuevo = new Libro(isbn,titulo,anioPublicacion,this.buscarEditorial(identificadorEditorial),this.buscarTipo(codigoTipo));
			this.listaLibros.add(nuevo);
			return true;
		}
		else
		{
			System.out.println("\nError al registrar el libro: "+titulo+", ISBN ya existe o alguno de los argumentos son invalidos.");
			return false;
		}
	}

/*..............................................................................................*/
	public boolean deshabilitarLibro(BigInteger isbn){
		if(this.buscarLibro(isbn)!=null){
			this.buscarLibro(isbn).deshabilitar();
			return true;
		}
		else
		{
			return false;
		}
	}

/*..............................................................................................*/
	public boolean habilitarLibro(BigInteger isbn){
		if(this.buscarLibro(isbn)!=null){
			this.buscarLibro(isbn).habilitar();
			return true;
		}
		else
		{
			return false;
		}
	}
	
/*..............................................................................................*/	
	public Libro obtenerLibro(BigInteger isbn){
		for (Libro actual : this.getListaLibros()) {
			if (actual.getIsbn().compareTo(isbn)==0){
				return (Libro) actual.clone();
			}
		}
		System.out.println("Error. No se puede realizar la copia del libro con ISBN: "+isbn+" ya que no existe.");
		return null;
	}
	
/*..............................................................................................*/
	public boolean estaHabilitadoLibro(BigInteger isbn){
		if(this.obtenerLibro(isbn)!=null){
			return this.obtenerLibro(isbn).estaHabilitado();
		}
		else
		{
			System.out.println("Error. No existe Libro con isbn: "+isbn);
			return false;
		}
	}
	
/*..............................................................................................*/
	public boolean modificarLibro(BigInteger isbn, BigInteger isbnCorrecto, String titulo, int anioPublicacion,Editorial editorial, Tipo tipo ){
		if(this.buscarLibro(isbn)!=null && this.buscarLibro(isbnCorrecto)==null){
			this.buscarLibro(isbn).modificar(isbnCorrecto, titulo, anioPublicacion, editorial, tipo);
			return true;
		}
		else
		{
			System.out.println("Error al intentar modificar el Libro con ISBN: "+ isbn +". Posiblemente no exista o el nuevo ISBN solicitado: "+isbnCorrecto+" ya este registrado.");
			return false;
		}
	}
	
/*..............................................................................................*/
	public boolean prestarLibro(int dni, BigInteger isbn){
		this.buscarInfractores(); // Actualiza e inhabilita usuarios que no devolvieron algun libro en el plazo establecido
		if(this.buscarUsuario(dni)!=null && this.buscarLibro(isbn)!=null){
			this.buscarLibro(isbn).realizarPrestamo(this.buscarUsuario(dni)).modificarPlazo(this.getPlazo());
			return true;
		}
		else
		{
			System.out.println("No se puede realizar prestamo. El usuario o el libro no existen en la base de datos.");
			return false;
		}
		
	}
	
/*..............................................................................................*/
	public Libro buscarLibroMasLeido(){
		Libro masLeido=null;
		for (Libro actual : this.getListaLibros()) {
			if(actual.estaHabilitado()){
				if(masLeido==null){
					masLeido=actual;}
				if(actual.cantidadPrestamos()>masLeido.cantidadPrestamos()){
					masLeido = actual;}
			}
		}
		System.out.println("\nLibro Mas Leido:");
		masLeido.imprimirDatos();
		return (Libro) masLeido.clone();
	}

/*..............................................................................................*/	
	// Datos de Prestamos de un libro
	public ArrayList<Prestamo> datosPrestamosLibro(BigInteger isbn){
		if(this.buscarLibro(isbn)!=null){
			for (Prestamo actual : this.buscarLibro(isbn).obtenerCopiaDePrestamos()) {
				actual.mostrarDatos();
			}
			return this.buscarLibro(isbn).obtenerCopiaDePrestamos();
		}
		return null;
	}
	
	// Consultas
/*..............................................................................................*/
	public ArrayList<Libro> imprimirLibros(){
		ArrayList<Libro> copia = new ArrayList<Libro>();
		System.out.println("\nTodos los Libros:");
		for (Libro actual : this.getListaLibros()) {
			actual.imprimirDatos();
			copia.add((Libro) actual.clone());
		}
		return copia;
	}
	
/*..............................................................................................*/	
	/* Busca todos los libros */
	private ArrayList<Libro> buscarLibro(){
		ArrayList<Libro> resultado = new ArrayList<Libro>(); 
		
		for (Libro actual : this.getListaLibros()) {
			if(actual.estaHabilitado()){
				resultado.add(actual);
			}
		}
		return resultado;
	}

/*..............................................................................................*/
	/* Busca por ISBN */
	public Libro buscarLibroPorIsbn(BigInteger isbn){
		if(this.buscarLibro(isbn)!=null){
			this.buscarLibro(isbn).imprimirDatos();
			return (Libro) buscarLibro(isbn).clone();
		}
		else
		{
			return null;
		}
	}
	
	private Libro buscarLibro(BigInteger isbn){
		for (Libro actual : this.getListaLibros()) {
			if(actual.getIsbn().compareTo(isbn)==0){
				return actual;
			}
		}
		return null;
	}
	
/*..............................................................................................*/
	/* Busca por Editorial */
	public ArrayList<Libro> buscarLibroPorEditorial(int identificador){
		if(this.buscarEditorial(identificador)!=null){
			return buscarLibro(this.buscarEditorial(identificador));
		}
		return null;
	}

/*..............................................................................................*/
	public ArrayList<Libro> buscarLibroPorEditorial(String nombre){
		if(this.buscarEditorial(nombre)!=null){
			return buscarLibro(this.buscarEditorial(nombre));
		}
		return null;
	}

/*..............................................................................................*/
	private ArrayList<Libro> buscarLibro(Editorial editorial){
		ArrayList<Libro> resultado = new ArrayList<Libro>(); 
		for (Libro actual : this.getListaLibros()) {
			if(actual.getEditorial().equals(editorial)){
				actual.imprimirDatos();
				resultado.add((Libro)actual.clone());
			}
		}
		if(resultado.isEmpty()){System.out.println("\nNingun libro con editorial: "+editorial.getNombre());};
		return resultado;
	}

/*..............................................................................................*/
	/* Busca por Tipo */
	public ArrayList<Libro> buscarLibroPorTipo(int codigo){
		return buscarLibro(this.buscarTipo(codigo));
	}
/*..............................................................................................*/
	public ArrayList<Libro> buscarLibroPorTipo(String descripcion){
		return buscarLibro(this.buscarTipo(descripcion));
	}
/*..............................................................................................*/
	private ArrayList<Libro> buscarLibro(Tipo tipo){
		ArrayList<Libro> resultado = new ArrayList<Libro>(); 
		
		for (Libro actual : this.getListaLibros()) {
			if(actual.getTipo().equals(tipo)){
				resultado.add((Libro) actual.clone());
			}
		}
		if(resultado.isEmpty()){System.out.println("\nNingun libro con tipo: "+tipo.getDescripcion());};
		return resultado;
	}

/*..............................................................................................*/
	/* Busca por Nombre */
	public ArrayList<Libro> buscarLibroPorNombre(String nombre){
		ArrayList<Libro> resultado = new ArrayList<Libro>(); 
		System.out.println("\nBusqueda de Libros con nombre: "+ nombre);
		
		for (Libro actual : this.getListaLibros()) {
			if(actual.getTitulo().toLowerCase().compareTo(nombre.toLowerCase())==0){
				resultado.add((Libro)actual.clone());
			}
		}
		if(resultado.isEmpty()){System.out.println("\nNingun libro con el nombre: "+nombre);};
		return resultado;
	}

/*..............................................................................................*/
	/* Encontrar libro por frase en el nombre */
	private ArrayList<Libro> encontrarLibro(String frase){
		ArrayList<Libro> resultado = new ArrayList<Libro>(); 
		
		for (Libro actual : this.getListaLibros()) {
			if(actual.getTitulo().toLowerCase().contains(frase.toLowerCase())){
				resultado.add(actual);
			}
		}
		if(resultado.isEmpty()){System.out.println("\nNingun libro contiene la frase: "+frase);};
		return resultado;
	}
	
/*..............................................................................................*/
	/* Encontrar libro por frase en el nombre */
	public ArrayList<Libro> buscarLibroPorFrase(String frase){
		ArrayList<Libro> resultados = new ArrayList<Libro>();
		System.out.println("\nBusqueda de Libros que contengan: "+ frase);
		for (Libro actual : this.encontrarLibro(frase)) {
			resultados.add((Libro) actual.clone());
			System.out.println("\t"+actual.getTitulo());
		}
		
		return resultados;
	}

/*..............................................................................................*/
	public boolean cancelarPrestamo(BigInteger isbn){
		if(this.buscarLibro(isbn)!=null){
			this.buscarLibro(isbn).cancelarPrestamo();
			return true;
		}
		else
		{
			return false;
		}
	}
	
/*..............................................................................................*/
	public boolean disponibilidadLibro(BigInteger isbn){
		if(this.buscarLibro(isbn)!=null){
			return buscarLibro(isbn).disponiblePrestamos();
		}
		else
		{
			System.out.println("Error. No existe libro con el ISBN: "+isbn);
			return false;
		}
	}
	
/*..............................................................................................*/
	// Getters And Setters
	public String getNombre() {
		return nombre;
	}
	
	private ArrayList<Libro> getListaLibros() {
		return listaLibros;
	}

	private void setListaLibros(ArrayList<Libro> listaLibros) {
		this.listaLibros = listaLibros;
	}

	private ArrayList<Editorial> getListaEditoriales() {
		return listaEditoriales;
	}

	private void setListaEditoriales(ArrayList<Editorial> listaEditoriales) {
		this.listaEditoriales = listaEditoriales;
	}

	private ArrayList<Tipo> getListaTipos() {
		return listaTipos;
	}

	private void setListaTipos(ArrayList<Tipo> listaTipos) {
		this.listaTipos = listaTipos;
	}

	private ArrayList<Usuario> getListaUsuarios() {
		return listaUsuarios;
	}

	private void setListaUsuarios(ArrayList<Usuario> listaUsuarios) {
		this.listaUsuarios = listaUsuarios;
	}

	public int getPlazo() {
		return plazo;
	}

	private void setPlazo(int plazo) {
		this.plazo = plazo;
	}
	
}
